package org.superheroes.androidserver.net.server;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.StringTokenizer;

import org.superheroes.androidserver.io.VideoStream;
import org.superheroes.androidserver.net.RequestType;
import org.superheroes.androidserver.net.State;
import org.superheroes.androidserver.net.packets.RTPPacket;
import org.superheroes.androidserver.thread.TimerThread;
import org.superheroes.androidserver.net.server.discovery.DiscoveryHandler;

import android.util.Log;

/**
 * @authors Jeffrey Goudzwaard, Davin Terrizzi
 * @version 1.0
 * @date 18-4-2013
 */

public class Server implements Runnable
{
	private static final String		LOG_TAG			= Server.class.getName();
	private static final String		LOG_ERROR_TAG	= Server.class.getName() + ".error";

	private static final String		CRLF			= "\r\n";

	private static Server			INSTANCE;

	private static int				MJPEG_TYPE		= 26;
	private static int				FRAME_PERIOD	= 100;
	private static int				VIDEO_LENGTH	= 500;
	private static int				id				= 123456;

	private static BufferedReader	READER;
	private static BufferedWriter	WRITER;

	private static String			videoFileName;

	private DatagramSocket			ds;

	private Socket					socket;

	private DatagramPacket			dgp;

	private InetAddress				clientAddr;

	private int						destPort		= 0;
	private int						imageNr			= 0;
	private int						seqNr			= 0;

	private VideoStream				vs;

	private byte[]					buffer;

	private State					state;

	private Timer					timer;

	public class Timer extends TimerThread
	{

		public Timer( long interval )
		{
			super( interval );
		}

		@Override
		public void performAction()
		{
			if ( imageNr < VIDEO_LENGTH )
			{
				imageNr++;

				try
				{
					int imageLength = vs.getNextFrame( buffer );
					RTPPacket packet = new RTPPacket( MJPEG_TYPE, imageNr, imageNr * FRAME_PERIOD, buffer, imageLength );

					int packetLength = packet.getLenght();

					byte[] packetBits = new byte[ packetLength ];
					packet.getPacket( packetBits );

					dgp = new DatagramPacket( packetBits, packetLength, clientAddr, destPort );
					ds.send( dgp );

					packet.printHeader();

				}
				catch ( Exception e )
				{
					Log.e( LOG_ERROR_TAG, e.toString() );
				}
			}
			else
			{
				stop();
			}
		}
	}

	public static Server getServer()
	{
		if ( INSTANCE == null )
		{
			Log.i( LOG_TAG, "Creating new Server instance" );
			INSTANCE = new Server();
		}

		return INSTANCE;
	}

	private void initDiscovery() throws MalformedURLException, UnknownHostException
	{
		DiscoveryHandler discHandler = new DiscoveryHandler();
	}

	private Server()
	{
		try
		{
			initDiscovery();
		}
		catch ( MalformedURLException e )
		{
			e.printStackTrace();
		}
		catch ( UnknownHostException e )
		{
			e.printStackTrace();
		}
		
		init();
	}

	@Override
	public void run()
	{
		start();
	}

	private void init()
	{
		Log.i( LOG_TAG, "Initializing server..." );
		timer = new Timer( FRAME_PERIOD );

		buffer = new byte[ 15000 ];
	}

	public Timer getTimer()
	{
		return timer;
	}

	public void start()
	{
		Log.i( LOG_TAG, "Starting server..." );
	}

	public RequestType parseRequest()
	{
		RequestType requestType = RequestType.NULL;

		try
		{
			String requestLine = READER.readLine();

			Log.i( LOG_TAG, "RequestLine: " + requestLine );

			StringTokenizer tokens = new StringTokenizer( requestLine );
			String requestTypeString = tokens.nextToken();
			requestType = RequestType.valueOf( requestTypeString );

			Log.i( LOG_TAG, "RequestType: " + requestType.toString() );

			if ( requestType == RequestType.SETUP )
			{
				videoFileName = tokens.nextToken();
			}

			String seqNumLine = READER.readLine();
			Log.i( LOG_TAG, "SeqNumLine: " + seqNumLine );
			tokens = new StringTokenizer( seqNumLine );
			tokens.nextToken();
			seqNr = Integer.parseInt( tokens.nextToken() );

			String lastLine = READER.readLine();
			Log.i( LOG_TAG, "lastLine: " + lastLine );

			if ( requestType == RequestType.SETUP )
			{
				tokens = new StringTokenizer( lastLine );
				for ( int i = 0; i < 3; i++ )
				{
					tokens.nextToken();
				}
				destPort = Integer.parseInt( tokens.nextToken() );
			}
		}
		catch ( Exception e )
		{
			Log.e( LOG_ERROR_TAG, e.toString() );
		}

		return requestType;
	}

	public void sendResponse()
	{
		try
		{
			WRITER.write( "RTSP/1.0 200 OK" + CRLF );
			WRITER.write( "CSeq: " + seqNr + CRLF );
			WRITER.write( "Session: " + id + CRLF );
		}
		catch ( Exception e )
		{
			Log.e( LOG_ERROR_TAG, e.toString() );
		}
		finally
		{
			try
			{
				WRITER.flush();
			}
			catch ( IOException e )
			{
				Log.e( LOG_ERROR_TAG, e.toString() );
			}
		}
	}

	public void setServerState( State state )
	{
		this.state = state;
	}

	public State getServerState()
	{
		return state;
	}

	public DatagramSocket getDatagramSocket()
	{
		return ds;
	}

	public void setDatagramSocket( DatagramSocket ds )
	{
		this.ds = ds;
	}

	public Socket getSocket()
	{
		return socket;
	}

	public void setSocket( Socket socket )
	{
		this.socket = socket;
	}

	public void setClientIPAddress( InetAddress clientIPAddress )
	{
		this.clientAddr = clientIPAddress;
	}

	public void setBufferedReader( BufferedReader reader )
	{
		READER = reader;
	}

	public BufferedReader getReader()
	{
		return READER;
	}

	public void setBufferedWriter( BufferedWriter writer )
	{
		WRITER = writer;
	}

	public BufferedWriter getWriter()
	{
		return WRITER;
	}

	public void setVideo( VideoStream vs )
	{
		this.vs = vs;
	}

	public String getVideoFileName()
	{
		return videoFileName;
	}

}